/**
  ******************************************************************************
  * @file    main.c
  * @version V1.0.0
  * @date    2024-12-01
  * @brief   函数入口文件
  *
  ******************************************************************************
  * @note
  *
  *
  ******************************************************************************
  */

/* Includes ------------------------------------------------------------------*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include "miio_crc32.h"

/**
  * @brief  提取文件名
  * @param  path: 提取的文件路径
  * @param  file_name: 存放提取的文件名
  * @retval None
  */
void file_extract_name(const char *path, char *file_name)
{
    const char *win_slash;
    const char *linux_slash;

    win_slash = strrchr(path, '\\');
    linux_slash = strrchr(path, '/');

    if (win_slash != NULL && linux_slash != NULL) {
        if (win_slash > linux_slash) {
            strcpy(file_name, win_slash + 1);
        } else {
            strcpy(file_name, linux_slash + 1);
        }
    } else if (win_slash != NULL) {
        strcpy(file_name, win_slash + 1);
    } else if (linux_slash != NULL) {
        strcpy(file_name, linux_slash + 1);
    } else {
        strcpy(file_name, path);
    }
}

/**
  * @brief  判断文件的拓展名是否正确
  * @param  file_name: 需要判断的文件名
  * @param  extension: 需要比对的拓展名
  * @retval 正确: 0, 错误: -1
  */
int file_has_extension(const char *file_name, const char *extension)
{
    const char *dot = strrchr(file_name, '.');

    if (dot == NULL || strcmp(dot + 1, extension) != 0) {
        return -1;
    }

    return 0;
}

/**
  * @brief  bin文件添加CRC32
  * @param  path: 需要加CRC32的bin文件路径
  * @retval 成功: 0, 失败: -1
  */
int file_bin_add_crc32(const char *path)
{
    uint8_t buf[128];       /* XMODEM协议按128字节处理 */
    uint8_t fw_info[12];
    uint32_t result;
    size_t i, dat_len, length;
    char *old_filename;
    char *new_filename;
    FILE *src_fp, *dst_fp;

    /* 判断文件是不是bin文件 */
    if (file_has_extension(path, "bin") != 0) {
        printf("%s not a bin file!\r\n", path);
        goto safe_exit0;
    }

    /* 以只读方式打开源文件 */
    src_fp = fopen(path, "rb");
    if (src_fp == NULL) {
        printf("%s open failed!\r\n", path);
        goto safe_exit0;
    }

    /* 去除源文件文件路径, 将文件名存放在old_filename里面 */
    length = strlen(path) + 1;
    old_filename = (char *)malloc(length);
    if (old_filename == NULL) {
        printf("%s old_filename malloc failed!\r\n", path);
        goto safe_exit1;
    }
    file_extract_name(path, old_filename);
//    printf("old_filename: %s\r\n", old_filename);

    /* 以提取文件名称创建新的bin文件名称 */
    length = strlen(old_filename) + 10;
    new_filename = (char *)malloc(length);
    if (new_filename == NULL) {
        printf("%s new_filename malloc failed!\r\n", path);
        goto safe_exit2;
    }

    length = strlen(old_filename) - 4;
    memcpy(new_filename, old_filename, length);
    new_filename[length] = '\0';
    strcat(new_filename, "_crc32");
    strcat(new_filename, ".bin");
//    printf("new_filename: %s\r\n", new_filename);

    /* 以新名称创建文件 */
    dst_fp = fopen(new_filename, "wb+");
    if (dst_fp == NULL) {
        printf("%s open failed!\r\n", new_filename);
        goto safe_exit3;
    }

    /* 将旧文件内容写入到新文件里面 */
    do {
        dat_len = fread(buf, 1, sizeof(buf), src_fp);
        fwrite(buf, 1, dat_len, dst_fp);
    } while (dat_len != 0);

    /* 计算新文件的大小 */
    fseek(dst_fp, 0, SEEK_END);
    dat_len = ftell(dst_fp);

    /* 新文件不足128字节的地方用0xFF补齐 */
    memset(buf, 0xFF, sizeof(buf));
    if ((dat_len % 128) != 0) {
        dat_len = 128 - (dat_len % 128);
        fwrite(buf, 1, dat_len, dst_fp);
    }

    /* 添加4字节bin文件大小 */
    dat_len = ftell(dst_fp);
    for (i = 0; i < 4; i++) {
        fw_info[i] = (uint8_t)(dat_len >> (i * 8));
    }

    /* 添加4字节CRC32用于MCU校验 */
    result = 0;
    fseek(dst_fp, 0, SEEK_SET);
    do {
        dat_len = fread(buf, 1, sizeof(buf), dst_fp);
        result = miio_crc32(buf, dat_len, result);
    } while (dat_len != 0);

    /* 固件长度计算CRC32 */
    result = miio_crc32(fw_info, 4, result);
    for (i = 0; i < 4; i++) {
        fw_info[i + 4] = (uint8_t)(result >> (i * 8));
    }

    /* 添加4字节CRC32用于小米平台校验 */
    result = miio_crc32(fw_info + 4, 4, result);
    for (i = 0; i < 4; i++) {
        fw_info[i + 8] = (uint8_t)(result >> (i * 8));
    }

    /* 新文件的结尾位置添加12字节的固件信息 */
    fwrite(fw_info, 1, 12, dst_fp);

    /* 释放内存 */
    free(old_filename);
    free(new_filename);
    fclose(src_fp);
    fclose(dst_fp);

    return 0;

safe_exit3:
    free(new_filename);

safe_exit2:
    free(old_filename);

safe_exit1:
    fclose(src_fp);

safe_exit0:
    return -1;
}

/**
  * @brief  应用程序入口函数
  * @param  argc: 接收得到的命令参数个数
  * @param  argv: 接收得到的命令参数数据
  * @retval 成功: 0, 失败: -1
  */
int main(int argc, char *argv[])
{
    int i;

    if (argc > 1) {
        for (i = 1; i < argc; i++) {
            file_bin_add_crc32(argv[i]);
        }
    } else {
        /* 不指定文件按默认文件fw.bin进行CRC32校验 */
        file_bin_add_crc32("fw.bin");
    }

    return 0;
}

